
//package java_cup;

/**
 * The "core" of an LR item. This includes a production and the position of a
 * marker (the "dot") within the production. Typically item cores are written
 * using a production with an embedded "dot" to indicate their position. For
 * example:
 * 
 * <pre>
 *     A ::= B * C d E
 * </pre>
 * 
 * This represents a point in a parse where the CUPParser is trying to match the
 * given production, and has succeeded in matching everything before the "dot"
 * (and hence is expecting to see the EmitSymbols after the dot next). See
 * lalr_item, lalr_item_set, and lalr_start for full details on the meaning and
 * use of ItemSet.
 *
 * @see java_cup.lalr_item
 * @see java_cup.lalr_item_set
 * @see java_cup.LALRState
 * @version last updated: 11/25/95
 * @author Scott Hudson
 */

public class LRItemCore {

    /*-----------------------------------------------------------*/
    /*--- Constructor(s) ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Full constructor.
     * 
     * @param production production this item uses.
     * @param dotPosition  position of the "dot" within the item.
     */
    public LRItemCore(ProductionDraft production, int dotPosition) /*throws internal_error*/ {
        if (production == null)
            throw new InternalError("Attempt to create an lr_item_core with a null production");
        if (dotPosition < 0 || dotPosition > _productionDraft.rhsLength)
            throw new InternalError("Attempt to create an lr_item_core with a bad dot position");

        _productionDraft = production;
        _dotPosition = dotPosition;

        /* compute and cache hash code now */
        _core_hash_cache = 13 * _productionDraft.GetHashCode() + dotPosition;

        /* cache the SymbolDraft after the dot */
        if (_dotPosition < _productionDraft.rhsLength) {
            ProductionPart part = _productionDraft.rhs(_dotPosition);
            if (!part.IsAction) { _symbolAfterDot = ((SymbolPart)part).TheSymbolDraft; }
        }
    }

    ///* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    ///**
    // * Constructor for dot at start of _right hand side.
    // * 
    // * @param production production this item uses.
    // */
    //public LRItemCore(ProductionDraft prod)
    //      /*throws internal_error*/
    //      : this(prod, 0) {
    //}

    /*-----------------------------------------------------------*/
    /*--- (Access to) Instance Variables ------------------------*/
    /*-----------------------------------------------------------*/

    /** The production for the item. */
    public ProductionDraft _productionDraft;

    /** The production for the item. */
    public ProductionDraft ProductionDraft => _productionDraft;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * The position of the "dot" -- this indicates the part of the production that
     * the marker is before, so 0 indicates a dot at the beginning of the RHS.
     */
    public int _dotPosition;

    /**
     * The position of the "dot" -- this indicates the part of the production that
     * the marker is before, so 0 indicates a dot at the beginning of the RHS.
     */
    public int DotPosition => _dotPosition;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Cache of the hash code. */
    public int _core_hash_cache;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Cache of SymbolDraft after the dot. */
    public SymbolDraft? _symbolAfterDot = null;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Is the dot at the end of the production? */
    public bool dotAtEnd => _dotPosition >= _productionDraft.rhsLength;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Return the SymbolDraft after the dot. If there is no SymbolDraft after the dot we
     * return null.
     */
    public SymbolDraft? SymbolAfterDot =>
        /* use the cached SymbolDraft */
        _symbolAfterDot;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Determine if we have a dot before a non terminal, and if so which one (return
     * null or the non terminal).
     */
    public Vn? VnAfterDot() {
        /* get the SymbolDraft after the dot */
        var symbol = this.SymbolAfterDot;

        /* if it exists and is a non terminal, return it */
        if (symbol != null && symbol.IsVn)
            return (Vn)symbol;
        else
            return null;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    ///**
    // * Produce a new lr_item_core that results from shifting the dot one position to
    // * the _right.
    // */
    //public LRItemCore ShiftCore() /*throws internal_error*/ {
    //    if (dotAtEnd)
    //        throw new InternalError("Attempt to Shift past end of an lr_item_core");

    //    return new LRItemCore(_productionDraft, _dotPosition + 1);
    //}

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Equality comparison for the core only. This is separate @out because we need
     * separate access in a base class.
     */
    public bool coreEquals(LRItemCore other) {
        return other != null && _productionDraft.Equals(other._productionDraft) && _dotPosition == other._dotPosition;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Equality comparison. */
    public bool equals(LRItemCore other) {
        return coreEquals(other);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Generic equality comparison. */
    /*@Override*/
    public override bool Equals(Object? other) {
        if (!(other is LRItemCore))
            return false;
        else
            return equals((LRItemCore)other);
    }

    ///* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    ///** Hash code for the core (separated so we keep non overridden version). */
    //public int coreHashCode() {
    //    return _core_hash_cache;
    //}

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Hash code for the item. */
    /*@Override*/
    public override int GetHashCode() {
        return _core_hash_cache;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    ///**
    // * Return the hash code that object would have provided for us so we have a
    // * (nearly) unique id for debugging.
    // */
    //public int objHash() {
    //    return base.GetHashCode();
    //}

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Convert to a string (separated @out from ToString() so we can call it from
     * subclass that overrides ToString()).
     */
    public String toSimpleString() /*throws internal_error*/ {
        String result;
        ProductionPart part;

        if (_productionDraft.lhs != null && _productionDraft.lhs.TheSymbolDraft != null
            && _productionDraft.lhs.TheSymbolDraft.Name != null)
            result = _productionDraft.lhs.TheSymbolDraft.Name;
        else
            result = "$$NULL$$";

        result += " ::= ";

        for (int i = 0; i < _productionDraft.rhsLength; i++) {
            /* do we need the dot before this one? */
            if (i == _dotPosition)
                result += "\u00B7 ";

            /* print the name of the part */
            if (_productionDraft.rhs(i) == null) {
                result += "$$NULL$$ ";
            }
            else {
                part = _productionDraft.rhs(i);
                if (part == null)
                    result += "$$NULL$$ ";
                else if (part.IsAction)
                    result += "{ACTION} ";
                else if (((SymbolPart)part).TheSymbolDraft != null && ((SymbolPart)part).TheSymbolDraft.Name != null)
                    result += ((SymbolPart)part).TheSymbolDraft.Name + " ";
                else
                    result += "$$NULL$$ ";
            }
        }

        /* put the dot after if needed */
        if (_dotPosition == _productionDraft.rhsLength)
            result += "\u00B7 ";

        return result;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Convert to a string */
    /*@Override*/
    public override string ToString() {
        /* can't throw here since base class doesn't, so we crash instead */
        try {
            return toSimpleString();
        }
        catch (InternalError e) {
            e.crash();
            return null;
        }
    }

    /*-----------------------------------------------------------*/

}
